<!DOCTYPE html>
<head>
    <meta charset="utf-8">
    <title>fabric-test1</title>

    <style>
        /* 容器，相对定位 */
        .box {
            position: relative;
        }

        /* 画布，给个边框 */
        #canvas {
            border: 1px solid #ccc;
        }

        /* 菜单 */
        .menu-x {
            visibility: hidden; /* 隐藏菜单 */
            z-index: -100;
            position: absolute;
            top: 0;
            left: 0;
            box-sizing: border-box;
            border-radius: 4px;
            box-shadow: 0 0 4px rgba(0, 0, 0, 0.3);
            background-color: #fff;
        }

        /* 菜单每个选项 */
        .menu-li {
            box-sizing: border-box;
            padding: 4px 8px;
            border-bottom: 1px solid #ccc;
            cursor: pointer;
        }

        /* 鼠标经过的选项，更改背景色 */
        .menu-li:hover {
            background-color: antiquewhite;
        }

        /* 第一个选项，顶部两角是圆角 */
        .menu-li:first-child {
            border-top-left-radius: 4px;
            border-top-right-radius: 4px;
        }

        /* 最后一个选项，底部两角是圆角，底部不需要边框 */
        .menu-li:last-child {
            border-bottom: none;
            border-bottom-left-radius: 4px;
            border-bottom-right-radius: 4px;
        }
    </style>
    <script src="fabric6.4.3.min.js"></script>
</head>
<body style="margin:0;padding:0;border:0">

<div class="header" style="width: 100%;height:48px;background-color:#eeeeee">
    <span id="hd_zoom">1</span>
    <p style="position: absolute;top:0;right:0">按ALt可以拖动画布，按ctrl+0可以重置画布缩放</p>
</div>
<div class="leftMenu"
     style="background-color:#aaaaaa;width: 300px;position: absolute;top:48px;left:0;height:calc(100vh - 48px)">

</div>
<!-- 容器（相对定位） -->
<div class="box"
     style="position:absolute;left:300px;top:48px;height: calc(100vh - 48px);width:calc(100vw - 300px);">
    <div style="position:relative;width:100%;height:100%;overflow:auto;scrollbar-width:thin;">
        <!-- 画布  -->
        <canvas id="canvas" width="600" height="600"></canvas>

        <!-- 右键菜单（绝对定位，且默认是隐藏的） -->
        <div
                id="menu"
                class="menu-x"
        >
            <div class="menu-li">什么都不做</div>
            <div class="menu-li">什么都不做</div>
            <div class="menu-li">什么都不做</div>
            <div class="menu-li">什么都不做</div>
            <div class="menu-li" onclick="delEl()">删除</div>
        </div>
    </div>
</div>


<script>
    let canvas = null;
    let targetData = {
        zoom: 1,
        lastName: "弦",
        age: 152
    };
    //用html5的proxy 把targetData.zoom 与 hd_zoom 绑定显示


    const hd_zoom = document.querySelector("#hd_zoom");
    const handleData = {
        get: (object, prop) => {
            console.log(object, prop);
        },
        set: (object, prop, value) => {
            if (prop === "zoom") {
                hd_zoom.textContent = value;
            }
            console.log(object, prop, value);
            return true;
        }
    }
    const proxyHandler = new Proxy(targetData, handleData);
    console.log(proxyHandler);


    //页面按ctrl+0 时 proxyHandler.zoom的值改位1
    window.addEventListener("keydown", function (e) {
        // console.log(e.code,  e.code === "Digit0",e.ctrlKey);
        if (e.ctrlKey && e.code === "Digit0") {
            canvas.setViewportTransform([1, 0, 0, 1, 0, 0]);
            proxyHandler.zoom = 1;
        }
    });

    function init2() {
        const canvas = new fabric.Canvas(canvas);

        fabric.InteractiveFabricObject.ownDefaults = {
            ...fabric.InteractiveFabricObject.ownDefaults,
            cornerStyle: 'round',
            cornerStrokeColor: 'blue',
            cornerColor: 'lightblue',
            cornerStyle: 'circle',
            padding: 10,
            transparentCorners: false,
            cornerDashArray: [2, 2],
            borderColor: 'orange',
            borderDashArray: [3, 1, 3],
            borderScaleFactor: 2,
        }

        const text = new fabric.FabricText('Fabric.JS');
        const rect = new fabric.Rect({width: 100, height: 100, fill: 'green'});

        canvas.add(text, rect);
        canvas.centerObject(text);
        canvas.setActiveObject(text);
    }


    function dropNewByElement(canvas, element) {
        canvas.on('drop', elt => {
            // 画布元素距离浏览器左侧和顶部的距离
            let offset = {
                left: canvas.getSelectionElement().getBoundingClientRect().left,
                top: canvas.getSelectionElement().getBoundingClientRect().top
            }
            // 鼠标坐标转换成画布的坐标（未经过缩放和平移的坐标）
            let point = {
                x: elt.e.x - offset.left,
                y: elt.e.y - offset.top,
            }
            // 转换后的坐标，restorePointerVpt 不受视窗变换的影响
            let pointerVpt = this.canvas.restorePointerVpt(point)
            console.log(pointerVpt)
            //创建元素
            // this.createElement(this.imageAddress, pointerVpt)
        });

    }

    function  addFormData(canvas){

        for (let i = 0; i < 258; i++) {
            const rect = new fabric.Rect({
                left: 0,
                top: 0,
                width: 300,
                height: 20,
                fill: "#FFFFFF",
                stroke: '#eee',
                strokeWidth: 2,
                hasRotatingPoint:false,
            });

            const text = new fabric.Textbox('Correctly loaded lato'+i, {
                left: 10,
                top: 0,
                width: 300,
                fontSize: 18,
                hasRotatingPoint:false,
            });

            const row = parseInt(i / 4);
            const cel = i % 4;
            const left = cel * (canvas.width/4 + 10)+ 10 ;
            const top = row * (24 + 10)  + 10 ;

            const group = new fabric.Group([rect,text], {
                //启用可拖动性
                draggable: true,
                //防止元素拖出画布
                clipTo: canvas,

                //限制元素在特定区域内拖动
                lockMovementX: false,
                lockMovementY: false,

                //限制元素沿水平或垂直方向拖动
                lockUniScaling: true,

                // 阻止元素旋转
                hasControls: false,
                //元素在拖动时不发生缩放
                lockScalingX: true,
                lockScalingY: true,

                _x_left: left,
                _x_top: top,
                hasRotatingPoint:false,
                left: left,
                top: top})

            // group.controls = {
            //     ...fabric.Text.prototype.controls,
            //     mtr: new fabric.Control({ visible: false })
            // }

            //事件监听器
            group.on('dragstart', function(e) {
                // 拖动开始时执行的操作
                console.log('dragstart', e)
            });

            group.on('dragmove', function(e) {
                // 拖动过程中执行的操作
                console.log('dragmove', e)
            });

            group.on('dragend', function(e) {
                // 拖动结束时执行的操作
                console.log('dragend', e)
            });



            //鼠标坐标
            //canvas.getPointer(e) 获取鼠标当前坐标。
            //坐标转换
            //var point = rect.getPointByOrigin(pointer);
            // 更新元素坐标
            //  group.set({
            //      left: point.x,
            //      top: point.y,
            //  });
            canvas.add(group);
        }


        // var textEditable = new fabric.Textbox(
        //     'Editable Textbox', {
        //         left: canvas.width / 2,
        //         width: 500,
        //         editable: true
        //     });
        // //给textEditable 添加边框
        // textEditable.setControlsVisibility({
        //     mt: false,
        //     mb: false,
        //     ml: false,
        //     mr: false,
        //     bl: false,
        //     br: false,
        //     tl: false,
        //     tr: false
        // });

        // //添加边框 边框一直显示
        // var textNonEditable = new fabric.Textbox(
        //     'Non Editable Textbox', {
        //         left: canvas.width / 2,
        //         top: 50,
        //         border: 2,
        //         borderColor: '#ff0000',
        //         strokeWidth: 1,
        //         stroke: "#AAAAAA",
        //         width: 500,
        //         editable: false
        //     });
        //
        // // Render the Textbox in canvas
        // canvas.add(textNonEditable);
        // canvas.add(textEditable);


        // let text = new fabric.Text('雷猴啊', {
        //     top: 310,
        //     left: 30,
        //     // fill: 'orange',
        //     stroke: '#333',
        //     strokeWidth: 2,
        //     // strokeUniform: true // 在 Text 里不生效！！！
        // })
        // canvas.add(rect1, text)

        const rect001 = new fabric.Rect({

            fill: '#ffffff',
            width: 100,
            height: 24,
            strokeWidth: 1,
            stroke: "#AAAAAA",
            rx: 10,
            ry: 10,
            angle: 0,
            scaleX: 1,
            scaleY: 1,
            hasControls: false
        });
        // canvas.add(rect001);

        // // 矩形
        // const rect1 = new fabric.Rect({
        //     left: 10,
        //     top: 510,
        //     fill: 'orange',//hotpink 亮粉色
        //     width: 40,
        //     height: 40
        // })
        // // 圆角矩形
        // const rect2 = new fabric.Rect({
        //     left: 510,
        //     top: 10,
        //     fill: 'pink',
        //     width: 40,
        //     height: 40,
        //     rx: 10, // 圆角x
        //     ry: 10, // 圆角y
        // })

        // // 圆形
        // const circle = new fabric.Circle({
        //     radius: 30, // 半径
        //     fill: 'green',// 黄绿色'yellowgreen'
        //     left: 20,
        //     top: 20,
        // })
        //


        // // 三角形
        // let triangle = new fabric.Triangle({
        //     width: 80, // 底边宽度
        //     height: 100, // 底边到定点的距离
        //     fill: 'blue', 天蓝色 skyblue
        //     left: 500,
        //     top: 480
        // })
        //
        // // 将矩形添加到画布中
        // canvas.add(rect1, rect2, circle, triangle)

    }
    function  moveCanvas(canvas) {
        // //移动画布
        // // 按下鼠标事件
        // canvas.on('mouse:down', function (opt) {
        //     var evt = opt.e;
        //     if (evt.altKey === true) {
        //         this.isDragging = true
        //         this.lastPosX = evt.clientX
        //         this.lastPosY = evt.clientY
        //     }
        // })
        //
        // // 移动鼠标事件
        // canvas.on('mouse:move', function (opt) {
        //     if (this.isDragging) {
        //         var e = opt.e;
        //         var vpt = this.viewportTransform;
        //         vpt[4] += e.clientX - this.lastPosX
        //         vpt[5] += e.clientY - this.lastPosY
        //         this.requestRenderAll()
        //         this.lastPosX = e.clientX
        //         this.lastPosY = e.clientY
        //     }
        // })
        //
        // // 松开鼠标事件
        // canvas.on('mouse:up', function (opt) {
        //     this.setViewportTransform(this.viewportTransform)
        //     this.isDragging = false
        // })



        var panning = false;
        //鼠标按下
        canvas.on('mouse:down', function (e) {
            //按住alt键才可拖动画布
            if(e.e.altKey) {
                panning = true;
                canvas.selection = false;
            }
        });

        //鼠标抬起
        canvas.on('mouse:up', function (e) {
            if(panning) {
                panning = false;
                canvas.selection = true;
            }else{
                console.log('mouse:up', e);
            }
        });

        //鼠标移动
        canvas.on('mouse:move', function (e) {
            if (panning && e && e.e) {
                const delta = new fabric.Point(e.e.movementX, e.e.movementY);
                canvas.relativePan(delta);
            }
        });
    }

    function canvasZoom(canvas) {
        //鼠标滚动画布放大缩小
        canvas.on('mouse:wheel', function (opt) {
            console.log('mouse:wheel', canvas.getZoom(), opt);
            if (opt.e.ctrlKey) {
                let zoom = (opt.e.deltaY > 0 ? -0.1 : 0.1) + canvas.getZoom();
                zoom = parseInt(zoom * 100) / 100;
                zoom = Math.max(0.2, zoom); //最小为原来的1/10
                zoom = Math.min(2, zoom); //最大是原来的3倍
                var zoomPoint = new fabric.Point(opt.e.pageX, opt.e.pageY);
                proxyHandler.zoom = zoom;
                // console.log(zoomPoint,zoom);
                canvas.zoomToPoint(zoomPoint, zoom);
                // zoom *= 1 + delta / 3000;
                // if (zoom > 20) zoom = 20;
                // if (zoom < 0.01) zoom = 0.01;
                // canvas.setZoom(zoom);
                // opt.e.preventDefault();
                // opt.e.stopPropagation();

            }
        });
    }




    // const option ={ left, right, top, lockMovementX:true, lockMovementY:true, };
    // const group = new fabric.Group(fabrics,option);
    //https://www.cnblogs.com/rachelch/p/13964837.html

    function moveObject(canvas) {
        // 监听对象移动
        canvas.on('object:moving', function (options) {

            // 通过 canvas.forEachObject 遍历画布上所有元素
            canvas.forEachObject(function (obj) {

                // 排除当前正在移动的元素
                if (obj === options.target) return

                // 检查对象是否与另一个对象相交
                if (options.target.intersectsWithObject(obj)) {
                    // 输出当前被接触的对象类型
                    // console.log(obj)
                    obj.opacity=0.5;
                    obj.set('_mv_obj',true);

                    // // 输出当前被接触的对象类型
                    // console.log(obj.type)
                    // obj['data-borderColor'] = obj.borderColor;
                    // obj.borderColor = "red";
                    // options.target.lockMovementX = true;
                    // options.target.lockMovementY = true;

                }else {
                    if (obj.get('_mv_obj')) {
                        obj.opacity = 1;
                        obj.borderColor = 'red';
                        obj.set('_mv_obj', false);
                    }
                }




            })
        });
    }

    function deleteObject(canvas) {
        // canvas.remove(object)
        // canvas.FX_DURATION = 1500;
        //   canvas.fxRemove(object, {
        //     onChange() {
        //       console.log('在动画的每一帧调用')
        //     },
        //     onComplete() {
        //       console.log('删除成功后调用')
        //     }
        //   })
    }
    // 初始化
    function init() {
        var canvasNode = document.querySelector("#canvas");
        canvasNode.width = window.innerWidth - 300 - 20;
        canvasNode.height = window.innerHeight - 48 - 20;
        canvas = new fabric.Canvas('canvas', {
            // allowTouchScrolling: true,
            originX: "left",
            originY: "top",
            fireRightClick: true, // 启用右键，button的数字为3
            stopContextMenu: true, // 禁止默认右键菜单
        })
        canvas.setHeight(3000);
        canvas.renderAll();
        moveCanvas(canvas);
        // dropNewByElement(canvas, canvasNode);
        addFormData(canvas);
        canvasZoom(canvas);
        moveObject(canvas);
        // //添加辅助线
        // const line = new fabric.Line([canvas.width / 2, 0, canvas.width / 2, canvas.height], {
        //     stroke: 'red',
        //     strokeWidth: 1,
        //     selectable: false,
        //     evented: false,
        //     hasBorders: false,
        //     hasControls: false,
        //     lockMovementX: true,
        //     lockMovementY: true,
        //     lockScalingX: true,
        //     lockScalingY: true,
        //     lockRotation: true,
        //     hoverCursor: 'default',
        // })
        // canvas.add(line);
        // canvas.height = 4000;
        // canvas.add(line);


        // 按下鼠标
        canvas.on('mouse:down', canvasOnMouseDown)
    }

    // 鼠标在画布上的点击事件
    function canvasOnMouseDown(opt) {

        // console.log(opt);
        // 判断：右键，且在元素上右键
        // opt.button: 1-左键；2-中键；3-右键
        // 在画布上点击：opt.target 为 null
        if ((opt.button === 3||(opt.e&&(opt.e.button===2))) && opt.target) {
            // 获取当前元素
            activeEl = opt.target
            console.log(activeEl);
            canvas.bringToFront(activeEl);

            menu.domReady = function () {
                console.log(123)
            }

            // 显示菜单，设置右键菜单位置
            // 获取菜单组件的宽高
            const menuWidth = menu.offsetWidth
            const menuHeight = menu.offsetHeight

            // 当前鼠标位置
            let pointX = opt.pointer.x
            let pointY = opt.pointer.y

            // 计算菜单出现的位置
            // 如果鼠标靠近画布右侧，菜单就出现在鼠标指针左侧
            if (canvas.width - pointX <= menuWidth) {
                pointX -= menuWidth
            }
            // 如果鼠标靠近画布底部，菜单就出现在鼠标指针上方
            if (canvas.height - pointY <= menuHeight) {
                pointY -= menuHeight
            }

            // 将菜单展示出来
            menu.style = `
      visibility: visible;
      left: ${pointX}px;
      top: ${pointY}px;
      z-index: 100;
    `
        } else {
            hiddenMenu()
        }
    }

    // 隐藏菜单
    function hiddenMenu() {
        menu.style = `
    visibility: hidden;
    left: 0;
    top: 0;
    z-index: -100;
  `
        activeEl = null
    }

    // 删除元素
    function delEl() {
        canvas.remove(activeEl)
        hiddenMenu()
    }

    // // 页面滚动事件不缩放网页大小，而是让canvas缩放
    // window.onscroll = function () {
    //     console.log('scroll')
    //     // var scrollTop = document.documentElement.scrollTop || document.body.scrollTop;
    //     // var scrollLeft = document.documentElement.scrollLeft || document.body.scrollLeft;
    //     // // console.log(scrollTop,scrollLeft);
    //     // // canvas.setViewportTransform([1,0,0,1,scrollLeft,scrollTop]);
    //     // // canvas.renderAll();  // 渲染所有元素
    //     // canvas.setViewportTransform([1, 0, 0, 1, scrollLeft, scrollTop]);
    //     // canvas.renderAll();  // 渲染所有元素
    //     // canvas.calcOffset();
    //     // scrollLeft = 0;
    //     // return false;
    // }


    // 文档加载后执行
    //+_
    window.onload = function () {

        // 输出当前 fabric 版本
        console.log(`Facrib.js版本：${fabric.version}`)

        // 初始化画布
        init()

        // 禁止在菜单上的默认右键事件
        menu.oncontextmenu = function (e) {
            //  e.preventDefault()
        }
        window.addEventListener('mousewheel', function (event) {
            if (event.ctrlKey === true || event.metaKey) {
                event.preventDefault();
            }
        }, {passive: false});

        //firefox
        window.addEventListener('DOMMouseScroll', function (event) {
            if (event.ctrlKey === true || event.metaKey) {
                event.preventDefault();
            }
        }, {passive: false});


        // document.addEventListener('keydown', function (event) {
        //     //注释
        //     if ((event.ctrlKey === true || event.metaKey === true)
        //         && (event.code === '=' || event.which === 107
        //             || event.which === 173 || event.which === 109
        //             || event.which === 187 || event.which === 189)) {
        //         event.preventDefault();
        //     }
        // }, false);
    }
</script>
</body>